#ifndef NODE_H_
#define NODE_H_
#endif /*NODE_H_*/

#ifndef VECTOR
#include <vector>
#endif

#ifndef NODEHEADER_H_
#include "nodeHeader.h"
#endif

#ifndef HYPO_H_
#include "hypo.h"
#endif

class node {
	public:

	int status;
	float status_rating;

	bool lock;
	bool froze;

	std::vector<hypo> hyp;
	std::vector<node*> linkedNodes; // how very paradoxical.
	std::vector<node*> linkingNodes;
	
	pthread_mutex_t    node_lock;
	pthread_mutex_t    node_hyp_lock;
	pthread_mutex_t    node_status_lock;
	pthread_mutex_t    node_linked_lock;
	pthread_mutex_t    node_linking_lock;
	pthread_mutex_t    node_processing_lock;
	pthread_mutex_t    node_status_rating_lock;

	node * temp;

	node( int count ) {
		this->status        = __DEFAULT_STATUS;
		this->status_rating = __DEFAULT_STATUS_RATING;
		this->lock          =        false;
		this->froze         =        false;		

		pthread_mutex_init( &this->node_lock,         NULL );
		pthread_mutex_init( &this->node_hyp_lock,     NULL );
		pthread_mutex_init( &this->node_linked_lock,  NULL );
		pthread_mutex_init( &this->node_status_lock,  NULL );
		pthread_mutex_init( &this->node_linking_lock,       NULL );
		pthread_mutex_init( &this->node_processing_lock,    NULL );
		pthread_mutex_init( &this->node_status_rating_lock, NULL );
		
		hypo h;
		for ( int i = 0; i < count; ++i ) {
			h.setHypo( i );
			this->hyp.push_back( h );
		}
	}
	
	bool evaluate() {
		int status, best;
		bool ret = false;
		pthread_mutex_lock( &this->node_processing_lock );
		if ( ! this->strictCheck() && ! this->isLocked() && !this->isFrozen() ) {
			status = this->getStatus();
			best = this->getBestStatus();
			if ( status != best ) {
				ret = true;
				if ( ! this->setStatus( best ) ) {
					std::cout << "Locked Node in Less then Best Position\n";
				}
				this->rerateThings();
			}	
		}
		pthread_mutex_unlock( &this->node_processing_lock );
		return ret;
	}
	bool inFrozenConflict() {
		pthread_mutex_lock( &this->node_linked_lock );
		bool rval = false;
		for ( unsigned int i = 0; i < this->linkedNodes.size(); ++i ) {
			temp = this->linkedNodes[i];
			if ( temp != NULL && temp->getStatus() == this->getStatus() && temp->isFrozen() ) {
				rval = true;
			}
		}
		pthread_mutex_unlock( &this->node_linked_lock );
		return rval;
	}
	void lockNode() {
		pthread_mutex_lock( &this->node_lock );
		this->lock = true;
		pthread_mutex_unlock( &this->node_lock );
	}
	void unlockNode() {
		pthread_mutex_lock( &this->node_lock );
		this->lock = false;
		pthread_mutex_unlock( &this->node_lock );
	}
	bool isLocked() {
		return this->lock;
	}
	void freezeNode()   { this->froze = true; }
	void unfreezeNode() { this->froze = false; }
	bool isFrozen() { return this->froze; }


	bool strictCheck() {
		pthread_mutex_lock( &this->node_linked_lock );
		bool rval = true;
		for ( unsigned int i = 0; i < this->linkedNodes.size(); ++i ) {
			temp = this->linkedNodes[i];
			if ( temp != NULL && temp->getStatus() == this->getStatus() ) {
				rval = false;
			}
		}
		pthread_mutex_unlock( &this->node_linked_lock );
		return rval;
	}
	bool hasLockedPeers() {
		bool ret = false;
		for ( unsigned int i = 0; i < this->linkedNodes.size(); ++i ) {
			if ( linkedNodes[i]->isLocked() ) {
				ret = true;
			}
		}
		for ( unsigned int i = 0; i < this->linkingNodes.size(); ++i ) {
			if ( linkingNodes[i]->isLocked() ) {
				ret = true;
			}
		}

		return ret;
	}
	void resetRate() {
		pthread_mutex_lock( &this->node_hyp_lock );
		for ( unsigned int i = 0; i < this->hyp.size(); ++i ) {
			this->hyp[i].reset();
		}
		pthread_mutex_unlock( &this->node_hyp_lock );
	}
	int getHypoCount() {
		return this->hyp.size();
	}
	void randomize() {
		unsigned int hc = this->getHypoCount();
		unsigned int status = ( rand() % hc );
		this->setStatus( status );
	}
	void rateHypoExcept( unsigned int h, float r ) {
		pthread_mutex_lock( &this->node_hyp_lock );
		if ( h <= this->hyp.size() ) {
			for ( unsigned int i = 0; i < this->hyp.size(); ++i ) {
				if ( h != i ) { this->hyp[i].rateHypo( r ); }
			}
		}
		pthread_mutex_unlock( &this->node_hyp_lock );
	}
	void rateHypo( unsigned int h, float r ) {
		pthread_mutex_lock( &this->node_hyp_lock );
		if ( h < this->hyp.size() ) {
			this->hyp[h].rateHypo( r );
		}
		pthread_mutex_unlock( &this->node_hyp_lock );
	}
	float getNegHypoRating( unsigned int h ) {
		pthread_mutex_lock( &this->node_hyp_lock );
		float ret = NULL;		
		if ( h <= this->hyp.size() ) {
			ret = this->hyp[h].getNeg();
		}
		pthread_mutex_unlock( &this->node_hyp_lock );
		return ret;
	}
	float getPosHypoRating( unsigned int h ) {
		pthread_mutex_lock( &this->node_hyp_lock );
		float ret = NULL;
		if ( h <= this->hyp.size() ) {
			ret = this->hyp[h].getPos();
		}
		pthread_mutex_unlock( &this->node_hyp_lock );
		return ret;
	}
	int getStatus() {
		return this->status;
	}
	bool setStatus( int status ) {
		bool ret = false;
		if ( ! this->isLocked() && ! this->isFrozen() ) {
			this->status = status;
			ret = true;
		}
		return ret;
	}
	void linkedNode( node * nodeski ) {
		pthread_mutex_lock( &this->node_linking_lock );
		if ( nodeski != NULL ) {
			this->linkingNodes.push_back( nodeski );
		}
		pthread_mutex_unlock( &this->node_linking_lock );
	}
	void linkNode( node * nodeski ) {
		if ( nodeski != NULL && nodeski != this ) {
			pthread_mutex_lock( &this->node_linked_lock );
			for ( unsigned int i = 0; i < this->linkedNodes.size(); ++i ) {
				if ( linkedNodes[i] == nodeski ) {
					return;
				}
			}
			this->linkedNodes.push_back( nodeski );
			nodeski->linkedNode( this );
			pthread_mutex_unlock( &this->node_linked_lock );
		}
	}
	float getStatusRating() {
		return this->status_rating;
	}
	int getLinkedCount() {
		return this->linkedNodes.size();
	}
	int getBestStatus() {
		if ( this->isFrozen() ) {
			return this->getStatus();
		} else {
			pthread_mutex_lock( &this->node_status_lock );
			int min = 0;
			int max = 0;
			int current = 0;
			int min_stat = 0;
			int max_stat = 0;
			int current_stat = 0;
			current      = hyp[this->status].getPos();
			current_stat = hyp[this->status].getHypo();
			for ( unsigned int i = 0; i < this->hyp.size(); ++i ) {
				if ( hyp[i].getPos() == max && hyp[i].getNeg() < min ) {
					max = hyp[i].getPos();
					max_stat = hyp[i].getHypo();					
				}
				if ( hyp[i].getPos() > max ) {
					max = hyp[i].getPos();
					max_stat = hyp[i].getHypo();
				}
				if ( hyp[i].getNeg() < min ) {
					min = hyp[i].getNeg();
					min_stat = hyp[i].getHypo();
				}
			}
			pthread_mutex_unlock( &this->node_status_lock );
			if ( max == current ) {
				return current_stat;
			} else {
				return max_stat;
			}
		}
	}
	void forceRate( node * nodeski ) {
		if ( nodeski != NULL && ! this->isFrozen() ) {
			nodeski->rateHypoExcept( this->status, 1 );
			nodeski->rateHypo( this->status, -1 );
		}
	}
	void cryForAttn() {
		for ( unsigned int i = 0; i < this->linkingNodes.size(); ++i ) {
			linkingNodes[i]->forceRate( this );
		}
	}
	void rerateThings() {
		if ( ! this->isFrozen() ) {
			for ( unsigned int i = 0; i < this->linkingNodes.size(); ++i ) {
				linkingNodes[i]->resetRate();
				linkingNodes[i]->cryForAttn();
				this->cryForAttn();
			}
			this->rateNodes();
		}
	}
	void rateNodes() {
		if ( ! this->isFrozen() ) {
			for ( unsigned int i = 0; i < this->linkedNodes.size(); ++i ) {
				temp = this->linkedNodes[i];
				if ( temp != NULL ) {
					temp->rateHypoExcept( this->status, __DEFAULT_UP_VALUE );
					temp->rateHypo( this->status, __DEFAULT_DOWN_VALUE );
				} 
			}
		}
	}
};

